import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { Icon } from '../icon';
import { Input } from '../input';
import { Tag } from '../tag';

type Options<TValue = OptionValues> = Array<IMultiOption<TValue>>;
export interface IMultiOption<TValue = OptionValues> {
  /** Text for rendering */
  label?: string;
  /** Value for searching */
  value?: TValue;
  disabled?: boolean;
}

type OptionValues = string;
export interface IMultiSelectProps {
  options: Options;
  value: IMultiOption[];
  onChange: (option: IMultiOption[] | null) => void;
  emptyTips?: React.ReactNode;
  name?: string;
  type?: string;
  onFocus?: React.FocusEventHandler<Element>;
  className?: string;
  large?: boolean; // 36px
  small?: boolean; // 26px
  disabled?: boolean;
  style?: React.CSSProperties;
}

export interface IMultiSelectState {
  drop: boolean;
  options: Options;
  filterOptions: Options;
  value: IMultiOption[];
}

export interface IMultiDropdownIndicatorProps {
  className?: string;
  disabled: boolean;
  opened: boolean;
}

const DropdownIndicator: React.SFC<IMultiDropdownIndicatorProps> = (props: IMultiDropdownIndicatorProps) => {
  const { opened } = props;
  const type = opened ? 'up' : 'down';
  return <div className={props.className}>
    <Icon type={type} /></div>;
};

DropdownIndicator.propTypes = {
  className: PropTypes.string,
  disabled: PropTypes.bool.isRequired,
  opened: PropTypes.bool.isRequired,
};

export interface IMultiDropdownListProps {
  emptyTips?: React.ReactNode;
  onFilterChange: (text: string) => void;
  options: Options;
  onInputClick: (e: React.MouseEvent<HTMLInputElement | HTMLDivElement>) => void;
  onItemClick: (option: IMultiOption, index: number) => void;
  value: IMultiOption[];
  type: string;
}

const DropdownList: React.SFC<IMultiDropdownListProps> = (props: IMultiDropdownListProps) => {
  const { onFilterChange, options, onInputClick, onItemClick, value, type } = props;
  let searchElement;
  if (type === 'search') {
    searchElement = <div className="br-select__search-box">
      <Input
        small
        onClick={onInputClick}
        autoFocus
        icon={<Icon type="search" size={12} color="#888" />}
        onChange={(e) => onFilterChange((e.target as { value: string }).value)}
      />
    </div>;
  }

  // TODO: 空数据时，需要文本提示
  let items: React.ReactNode = props.emptyTips ||
    <li className="br-select__menu-item br-select__menu-item--disabled">暂无数据</li>;

  if (options.length) {
    items = options.map((option, index) => {
      const isSelected = !!value.find((val) => val.value === option.value);

      return (
        <li key={index}
          className={classNames('br-select__menu-item', {
            'br-select__menu-item--selected': isSelected,
            'br-select__menu-item--disabled': option.disabled,
          })}
          onClick={(e) => onItemClick(option, index)}
        >
          {option.label}
        </li>
      );
    });
  }

  return (
    <div className="br-select__menu" onClick={onInputClick}>
      {searchElement}
      <ul className="br-select__menu-list">
        {items}
      </ul>
    </div>
  );
};
DropdownList.propTypes = {
  emptyTips: PropTypes.node,
  onFilterChange: PropTypes.func.isRequired,
  onInputClick: PropTypes.func.isRequired,
  onItemClick: PropTypes.func.isRequired,
  type: PropTypes.string.isRequired,
};
export interface IMultiValueViewProps {
  value: IMultiOption[];
  onClose?: React.FocusEventHandler<Element>;
  onTagClose?: (value: IMultiOption, index: number) => void;
  large?: boolean;
  small?: boolean;
}

const MultiValueView: React.SFC<IMultiValueViewProps> = ({...props}: IMultiValueViewProps) => {
  const tagClassName = {
    'br-select__tag--large': props.large,
    'br-select__tag--small': props.small,
  };
  // 复选时调用tag组件
  return <React.Fragment>
  {props.value.map((value, index) => <Tag
    key={index}
    className={ classNames('br-select__tag' , tagClassName)}
    onClose={() => props.onTagClose && props.onTagClose(value, index)}
    >{value.label}</Tag>)}</React.Fragment>;
};

MultiValueView.propTypes = {
  onTagClose: PropTypes.func,
  large: PropTypes.bool,
  small: PropTypes.bool,
};

export class MultiSelect extends React.Component<IMultiSelectProps, IMultiSelectState> {
  public static propTypes = {
    emptyTips: PropTypes.node,
    name: PropTypes.string,
    type: PropTypes.string,
    className: PropTypes.string,
    large: PropTypes.bool, // 36px
    small: PropTypes.bool, // 26px
    disabled: PropTypes.bool,
    style: PropTypes.object,
  };

  constructor(props: IMultiSelectProps) {
    super(props);
    this.state = {
      drop: false,
      options: this.props.options,
      filterOptions: this.props.options,
      value: [],
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleTagClose = this.handleTagClose.bind(this);
    this.handleFilterChange = this.handleFilterChange.bind(this);
    this.handleInputClick = this.handleInputClick.bind(this);
    this.handleItemClick = this.handleItemClick.bind(this);
  }

  public handleClick(e: any) {
    e.nativeEvent.stopImmediatePropagation();
    let dropFlag = !this.state.drop;
    if (dropFlag) {
      if (this.props.onFocus) {
        this.props.onFocus(e);
      }
    }
    this.setState({
      drop: dropFlag,
      filterOptions: this.props.options,
    });
  }

  public handleTagClose(value: IMultiOption, index: number) {
    if (this.state.value instanceof Array) {
      const newValues = [...this.state.value];
      newValues.splice(index, 1);
      if (this.props.onChange) {
        this.props.onChange(newValues);
      }
      this.setState({ value: newValues });
    }
  }

  public handleFilterChange(text: string) {
    let filterOptions = this.state.options;
    if (text && text.trim()) {
      filterOptions = this.state.options.filter((option) => {
        const label = option.label || option.value || '';
        return label.includes(text);
      });
    }
    this.setState(() => {
      return { filterOptions };
    });
  }

  public handleInputClick(e: React.MouseEvent<HTMLInputElement | HTMLDivElement>) {
    e.nativeEvent.stopImmediatePropagation();
  }

  public handleItemClick(option: IMultiOption, index: number) {
    const beforeVal = this.props.value || this.state.value;
    if (this.props.onChange) {
        if (beforeVal.find((val) => val.value === option.value)) {
          this.props.onChange(beforeVal.concat());
        } else {
          this.props.onChange(beforeVal.concat(option));
        }
    }
    const value = beforeVal.concat(option);
    this.setState({
      drop: !!option.disabled,
      filterOptions: this.props.options,
      value,
    });
  }

  public render() {
    let list = null;
    const value = this.props.value || this.state.value || []; // 判断是不是数组
    const values = value;
    if (this.state.drop) {
      list = <DropdownList
        options={this.state.filterOptions}
        onFilterChange={this.handleFilterChange}
        onInputClick={this.handleInputClick}
        onItemClick={this.handleItemClick}
        value={values}
        emptyTips={this.props.emptyTips}
        type={this.props.type ? this.props.type : 'normal'}
      />;
    }

    // 是否为disabled状态
    const disabled = this.props.disabled || false;

    const modifierClassName = {
      'br-select--large': this.props.large,
      'br-select--small': this.props.small,
      'br-select--disabled': disabled,
    };
    return (
      <div
        className={classNames('br-select', modifierClassName, this.props.className)}
        style={this.props.style}
      >
        <a className="br-select__controller" onClick={this.handleClick}>
          <span className="br-select__value">
            <MultiValueView
              large={this.props.large}
              small={this.props.small}
              value={value}
              onTagClose={this.handleTagClose}
            />
          </span>

          <DropdownIndicator
            className="br-select__indicator"
            disabled={disabled}
            opened={this.state.drop}
          />
        </a>
        {list}
      </div>
    );
  }
}
